module vmm

#flag -fPIC -DGC_THREADS -lgc -lpthread
// it just work, although v recogonize this error form
#flag -Wl,-wrap,malloc
#flag -Wl,-wrap,calloc
#flag -Wl,-wrap,realloc
#flag -Wl,-wrap,free
// #flag -Wl,-wrap,main

#flag -I@VROOT/
#include <stdio.h>
#include <stdlib.h>
#include "gc/gc.h"
#include "ldwrapmm.h" //!!!

/////
const hkdebug = false
const usegc = true
const szfix = size_t(3)

///// C.printf still our good friend

pub fn pre_main_init() {
    gcinit()
}
pub fn post_main_deinit() {
    //C.GC_disable()
    gcdeinit()
}

///////////
// GC_INIT() is a function like macro, but it works
fn C.GC_INIT() int

fn gcinit() {
    // too late here, but works
    if hkdebug {
        C.printf("gc init ... usegc=%d\n", usegc)
    }
    mut gcok := C.GC_is_init_called()

    C.GC_set_finalize_on_demand(0)
    C.GC_set_free_space_divisor(3) // default 3
    C.GC_set_dont_precollect(1)
    C.GC_set_dont_expand(0)
    C.GC_allow_register_threads()

    C.GC_INIT()
    // C.GC_init()
    C.GC_allow_register_threads()
    gcok = C.GC_is_init_called()
    if hkdebug {
        C.printf("gc init done %d usegc=%d\n", gcok, usegc)
    }
}
fn gcdeinit() {
    // it should safe use println here
    if hkdebug {
        println("gc deinit ...")
    }
    C.GC_gcollect()
    //C.GC_deinit()
    if hkdebug {
        println("gc deinit done")
    }
}

// wow, it works
/* fn C.__real_main(int, &byteptr) int */

/* [export: '__wrap_main'] */
/* fn __wrap_main(argc int, argv &byteptr) int { */
/*     if hkdebug { */
/*         println('ldwraphook main') */
/*     } */
/*     gcinit() */
/*     rv := C.__real_main(argc, argv) */
/*     C.GC_disable() */
/*     gcdeinit() */
/*     return rv */
/* } */

fn C.__real_malloc(size_t) voidptr

fn C.abort() int

///

[export: '__wrap_malloc' ]
fn __wrap_malloc(c size_t) voidptr {
    mut res := voidptr(0)
    c2 := c + szfix
    // println(@FN)
    if hkdebug {
        C.printf("ldwraphook%d malloc (%d) ...\n", usegc, c)
        if c == size_t(0) {
            //C.abort()
        }
    }
    if c == size_t(0) {
        return voidptr(0)
    }
    if usegc {
        res = C.GC_MALLOC(c2)
    }else {
        res = C.__real_malloc(c2)
    }
    // println(@FN)
    if hkdebug {
        C.printf("ldwraphook malloc (%d) %p\n", c, res)
    }
    return res
}

fn C.__real_calloc(size_t, size_t) voidptr

[export: '__wrap_calloc' ]
fn __wrap_calloc(n size_t, c size_t) voidptr {
    mut res := voidptr(0)
    c2 := c + szfix
    // println(@FN)
    if hkdebug {
        C.printf("ldwraphook%d calloc (%d, %d) ...\n", usegc, n, c)
    }
    if n*c == size_t(0) {
        // C.abort()
        return voidptr(0)
    }
    if usegc {
        res = C.GC_MALLOC(n * c2)
    }else{
        res = C.__real_calloc(n, c2)
    }
    // println(@FN)
    if hkdebug {
        C.printf("ldwraphook calloc (%d, %d) %p\n", n, c, res)
    }
    return res
}

fn C.__real_realloc(voidptr, size_t) voidptr

[export: '__wrap_realloc' ]
fn __wrap_realloc(ptr voidptr, c size_t) voidptr {
    mut res := voidptr(0)
    c2 := c + szfix
    // println(@FN)
    isgc := C.GC_is_heap_ptr(ptr)
    sz := if isgc > 0 { C.GC_size(ptr) } else { 0 }
    if hkdebug {
        C.printf("ldwraphook realloc (%p[%d], %d) ...\n", ptr, sz, c)
        if size_t(sz) >= c {
            // C.abort()
        }
    }
    if size_t(sz) >= c { return ptr }
    if usegc {
        res = C.GC_REALLOC(ptr, c2)
    }else{
        res = C.__real_realloc(ptr, c2)
    }
    // println(@FN)
    if hkdebug {
        C.printf("ldwraphook realloc (%p[%d], %d) %p\n", ptr, sz, c, res)
    }
    return res
}

// dummy return int to fix v parser error
fn C.__real_free(voidptr) int

[export: '__wrap_free' ]
fn __wrap_free(ptr voidptr) {
    // println(@FN)
    isgc := C.GC_is_heap_ptr(ptr)
    sz := if isgc > 0 { C.GC_size(ptr) } else { 0 }
    if hkdebug {
        C.printf("ldwraphook free (%p[%d])\n", ptr, sz)
    }
    if isgc > 0 {
        C.GC_FREE(ptr)
    }else{
        C.__real_free(ptr)
    }
}

///// real API

pub fn real_malloc(c int) voidptr {
    c2 := size_t(c) + szfix
    mut res := voidptr(0)
    res = C.__real_malloc(c2)
    return res
}
pub fn real_calloc(n int, c int) voidptr {
    c2 := size_t(c) + szfix
    mut res := voidptr(0)
    res = C.__real_calloc(n, c2)
    return res
}
pub fn real_realloc(ptr voidptr, c int) voidptr {
    c2 := size_t(c) + szfix
    mut res := voidptr(0)
    res = C.__real_realloc(ptr, c2)
    return res
}
pub fn real_free(ptr voidptr) {
    C.__real_malloc(ptr)
}

